package com.lx.wrapper.impl;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.ArrayUtil;
import com.lx.constant.ano.FieldFunction;
import com.lx.constant.consts.CommonConstant;
import com.lx.constant.consts.SqlFormatConstant;
import com.lx.constant.consts.SqlKeyWordConstant;
import com.lx.constant.enums.ColumnKeyWorldEnum;
import com.lx.constant.enums.PreConditionTypeEnum;
import com.lx.constant.enums.SegmentEnum;
import com.lx.constant.enums.SortWayEnum;
import com.lx.constant.execption.LambdaCheckException;
import com.lx.constant.execption.LambdaIllegalParamException;
import com.lx.constant.segment.Segment;
import com.lx.constant.segment.query.ColumnSegment;
import com.lx.constant.segment.query.GroupSegment;
import com.lx.constant.segment.query.OrderSegment;
import com.lx.inter.query.ResultConvertFunction;
import com.lx.inter.query.impl.DefaultResultConvertFunction;
import com.lx.wrapper.AbstractCommonConditionWrapper;
import com.lx.wrapper.AbstractMainEntityLambdaConditionWrapper;
import com.lx.wrapper.AbstractWrapper;
import org.hibernate.query.Query;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

/**
 * Lambda查询语句对象
 *
 * @author lx
 * @create 2022/12/28 16:40
 **/
public class LambdaQueryWrapper<MainEntity> extends AbstractMainEntityLambdaConditionWrapper<MainEntity, LambdaQueryWrapper<MainEntity>> implements ResultConvertFunction<LambdaQueryWrapper<MainEntity>> {

    /**
     * 结果转换代理类
     */
    protected final ResultConvertFunction<LambdaQueryWrapper<MainEntity>> resultConvertProxy;

    public LambdaQueryWrapper() {
        this(null);
    }

    public LambdaQueryWrapper(Class<MainEntity> entityClass) {
        super(entityClass);
        resultConvertProxy = new DefaultResultConvertFunction(this);
    }

    @Override
    public LambdaQueryWrapper<MainEntity> setConvertClass(Class<?> convertClass) {
        return this.resultConvertProxy.setConvertClass(convertClass);
    }

    /**
     * 获取转换结果类,如未设置转换类,则默认转换为 MainEntity 类型
     *
     * @return
     */
    @Override
    public Class<?> getConvertClass() {
        return this.resultConvertProxy.getConvertClass() == null ? checkAndGetValidEntityClass() : this.resultConvertProxy.getConvertClass();
    }

    public LambdaQueryWrapper<MainEntity> having() {
        return super.setPreConditionType(PreConditionTypeEnum.HAVING);
    }

    public LambdaQueryWrapper<MainEntity> groupBy(FieldFunction<MainEntity, ?>... fnn) {
        if (fnn == null) {
            return this;
        }
        for (FieldFunction<MainEntity, ?> fn : fnn) {
            groupBy(fn, null);
        }
        return this;
    }

    public LambdaQueryWrapper<MainEntity> groupBy(FieldFunction<MainEntity, ?> fn, SortWayEnum sortWay) {
        if (fn == null) {
            return this;
        }
        GroupSegment groupSegment = createAndAddSegment(new GroupSegment(), SegmentEnum.GROUP_BY, fn, null);
        groupSegment.setSortWay(sortWay == null ? SortWayEnum.ASC : sortWay);
        return this;
    }

    public LambdaQueryWrapper<MainEntity> orderBy(FieldFunction<MainEntity, ?>... fnn) {
        if (fnn == null) {
            return this;
        }
        for (FieldFunction<MainEntity, ?> fn : fnn) {
            orderBy(fn, null);
        }
        return this;
    }

    public LambdaQueryWrapper<MainEntity> orderBy(FieldFunction<MainEntity, ?> fn, SortWayEnum sortWay) {
        if (fn == null) {
            return this;
        }
        OrderSegment orderSegment = createAndAddSegment(new OrderSegment(), SegmentEnum.ORDER_BY, fn, null);
        orderSegment.setSortWay(sortWay == null ? SortWayEnum.ASC : sortWay);
        return this;
    }

    /**
     * 构建多条不存在别名的查询列名
     *
     * @param fnn
     * @return
     */
    public LambdaQueryWrapper<MainEntity> select(FieldFunction<MainEntity, ?>... fnn) {
        if (fnn == null) {
            return this;
        }
        for (FieldFunction<MainEntity, ?> fn : fnn) {
            select(fn, null);
        }
        return this;
    }

    /**
     * 构建单条存在别名的查询列名
     *
     * @param fnn
     * @param aliasName
     * @return
     */
    public LambdaQueryWrapper<MainEntity> select(FieldFunction<MainEntity, ?> fnn, String aliasName) {
        if (fnn == null) {
            return this;
        }
        return addColumnSegment(aliasName, null, null, fnn);
    }

    /**
     * 构建多条存在别名的查询列名
     *
     * @param aliasColumns
     * @return
     */
    public LambdaQueryWrapper<MainEntity> select(Pair<FieldFunction<MainEntity, ?>, String>... aliasColumns) {
        if (aliasColumns == null) {
            return this;
        }
        for (Pair<FieldFunction<MainEntity, ?>, String> aliasColumn : aliasColumns) {
            select(aliasColumn.getKey(), aliasColumn.getValue());
        }
        return this;
    }

    /**
     * 构建单条子查询列的列名,建议设置别名
     *
     * @param childQueryWrapper
     * @param aliasName         列名别名
     * @return
     */
    public LambdaQueryWrapper<MainEntity> selectChildQuery(AbstractCommonConditionWrapper childQueryWrapper, String aliasName) {
        if (childQueryWrapper == null) {
            return this;
        }
        return addColumnSegment(aliasName, childQueryWrapper, null);
    }

    public LambdaQueryWrapper<MainEntity> selectChildQuery(Pair<AbstractCommonConditionWrapper, String>... aliasChildQueryWrappers) {
        if (aliasChildQueryWrappers == null) {
            return this;
        }
        for (Pair<AbstractCommonConditionWrapper, String> childWrapperPair : aliasChildQueryWrappers) {
            selectChildQuery(childWrapperPair.getKey(), childWrapperPair.getValue());
        }
        return this;
    }

    /**
     * 增加通用列名语句
     * 场景:
     * 1.增加Select 普通列
     * 2.新增或替换Select 函数聚合列
     *
     * @param aliasName    聚合列别名
     * @param childWrapper 子查询项
     * @param keyWorldEnum
     * @param fnn
     * @return
     */
    protected LambdaQueryWrapper<MainEntity> addColumnSegment(String aliasName, AbstractCommonConditionWrapper childWrapper, ColumnKeyWorldEnum keyWorldEnum, FieldFunction<MainEntity, ?>... fnn) {
        if (ArrayUtil.isEmpty(fnn)) {
            return this;
        }
        //TODO 当前仅支持单个聚合语句写入.
        if (fnn.length > 1) {
            throw new LambdaIllegalParamException("当前仅支持单个聚合语句写入,请等待下次计划开发!");
        }
        FieldFunction<MainEntity, ?> firstFieldFunction = fnn[0];

        ColumnSegment dc = replaceOrAddSegment(new ColumnSegment(), SegmentEnum.COLUMN, firstFieldFunction, null);
        dc.setChildConditionWrapper(childWrapper);
        dc.setAliasName(aliasName);

        if (keyWorldEnum != null) {
            //设置列扩展属性列表
            dc.getColumnExtendList().add(keyWorldEnum);
        }
        return this;
    }

    /**
     * 列名字段聚合函数 = 去重
     *
     * @param aliasName 聚合列别名
     * @param fnn
     * @return
     */
    public LambdaQueryWrapper<MainEntity> distinct(String aliasName, FieldFunction<MainEntity, ?>... fnn) {
        return addColumnSegment(aliasName, null, ColumnKeyWorldEnum.DISTINCT, fnn);
    }

    /**
     * 列名字段聚合函数 = 求和
     *
     * @param fnn
     * @return
     */
    public LambdaQueryWrapper<MainEntity> sum(FieldFunction<MainEntity, ?>... fnn) {
        return sum(null, fnn);
    }

    /**
     * 列名字段聚合函数 = 求和
     *
     * @param aliasName 聚合列别名
     * @param fnn
     * @return
     */
    public LambdaQueryWrapper<MainEntity> sum(String aliasName, FieldFunction<MainEntity, ?>... fnn) {
        return addColumnSegment(aliasName, null, ColumnKeyWorldEnum.SUM, fnn);
    }

    /**
     * 列名字段聚合函数 = 总数
     *
     * @param aliasName 聚合列别名
     * @param fnn
     * @return
     */
    public LambdaQueryWrapper<MainEntity> count(String aliasName, FieldFunction<MainEntity, ?>... fnn) {
        return addColumnSegment(aliasName, null, ColumnKeyWorldEnum.COUNT, fnn);
    }

    /**
     * 列名字段聚合函数 = 平均
     *
     * @param aliasName 聚合列别名
     * @param fnn
     * @return
     */
    public LambdaQueryWrapper<MainEntity> avg(String aliasName, FieldFunction<MainEntity, ?>... fnn) {
        return addColumnSegment(aliasName, null, ColumnKeyWorldEnum.AVG, fnn);
    }

    @Override
    protected void setHibernateParamByQuery0(boolean isHQL, Query query) {
        //其他映射
        Map<SegmentEnum, List<Segment>> segmentsMap = getSegmentsByEnums(SegmentEnum.COLUMN, SegmentEnum.WHERE, SegmentEnum.LIMIT);
        segmentsMap.forEach((e1, l1) -> {
            if (CollectionUtil.isEmpty(l1)) {
                return;
            }
            for (Segment sn : l1) {
                sn.setHibernateParamByQuery(isHQL, query);
            }
        });
    }

    @Override
    protected void setCompleteQL0(boolean isHQL, String qlString) {
        Class<MainEntity> entityClass = getEntityClass();
        //用于映射后续的列名对象和数据封装. 如存在统一替代符号【*】,则必须指定实体类
        int index;
        {
            //大小写问题处理.
            String upQlString = qlString.toUpperCase();
            if (!upQlString.startsWith(SqlKeyWordConstant.SELECT) || (index = upQlString.indexOf(SqlKeyWordConstant.FROM)) == -1) {
                throw new LambdaCheckException("查询Wrapper下对应的语句必须为查询语句!");
            }
        }
        //解析属性相关数据
        String[] columnChildArray = qlString.substring(SqlKeyWordConstant.SELECT.length(), index).split(CommonConstant.COMMA);

        if (columnChildArray.length == 1 && columnChildArray[0].contains(CommonConstant.ASTERISK)) {
            //判断是否存在*号且只有一个解析字段.
            if (entityClass == null) {
                throw new LambdaCheckException("当查询语句中为不可变变量[*]时,实体类对象必须存在!");
            }
            //解析*符号
            List<Field> fields = explainColumnFieldListByEntityClass();
            for (Field field : fields) {
                createAndAddSegment(new ColumnSegment(), SegmentEnum.COLUMN, field, null);
            }
        }

        //解析单独构建的列名项
        for (String columnChild : columnChildArray) {
            //组成结构：列名 + 列别名(可能不存在)
            String[] columnNameAndAliasArr = columnChild.trim().split("( AS )|( as )|( aS )|( As )|[ ]");
            ColumnSegment tem = (ColumnSegment) createAndAddSegment(new ColumnSegment(), SegmentEnum.COLUMN, (Field) null, columnNameAndAliasArr[0]);
            if (columnNameAndAliasArr.length > 1) {
                tem.setAliasName(columnNameAndAliasArr[1]);
            }
        }
    }

    @Override
    protected String getCompleteQL0(boolean isHQL, AbstractWrapper parentWrapper) {
        StringBuilder sqlBuilder = new StringBuilder(512);
        //1.检查和构建查询列.
        if (!existDataFromSegmentEnum(SegmentEnum.COLUMN)) {
            //如不存在,则需要根据[EntityClass]推断所有列名
            List<Field> fields = explainColumnFieldListByEntityClass();
            for (Field field : fields) {
                createAndAddSegment(new ColumnSegment(), SegmentEnum.COLUMN, field, null);
            }
        }
        sqlBuilder.append(buildConditions(isHQL, parentWrapper, SegmentEnum.COLUMN));
        //2.构建表.
        sqlBuilder.append(SqlKeyWordConstant.FROM).append(String.format(SqlFormatConstant.AS_FORMAT, inferAndGetTableName(isHQL), getTableAliasName(false)));
        //3.构建条件项.
        sqlBuilder.append(buildConditions(isHQL, parentWrapper, SegmentEnum.WHERE));
        //4.构建分组项.
        sqlBuilder.append(buildConditions(isHQL, parentWrapper, SegmentEnum.GROUP_BY));
        //5.构建分组条件
        sqlBuilder.append(buildConditions(isHQL, parentWrapper, SegmentEnum.HAVING));
        //6.构建排序条件
        sqlBuilder.append(buildConditions(isHQL, parentWrapper, SegmentEnum.ORDER_BY));
        //7.构建分页条件
        sqlBuilder.append(buildConditions(isHQL, parentWrapper, SegmentEnum.LIMIT));
        return sqlBuilder.toString();
    }

    @Override
    public <Response> Response convertToResponse(Class<Response> resultClass, Object hibernateRes) {
        return resultConvertProxy.convertToResponse(resultClass, hibernateRes);
    }

    @Override
    public <Response> Response convertToResponse(Response response, Object hibernateRes) {
        return resultConvertProxy.convertToResponse(response, hibernateRes);
    }

    @Override
    public <Response> List<Response> convertToResponseList(List<Object> hibernateResList, Class<Response> resultClass) {
        return resultConvertProxy.convertToResponseList(hibernateResList, resultClass);
    }

}
